package zdream.pmfield.stage.object;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

import zdream.pmfield.data.foe.MoveAdditionalEffects;
import zdream.pmfield.data.foe.MoveCheckPair;
import zdream.pmfield.data.foe.MoveTarget;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.data.foe.MoveType;
import zdream.pmfield.data.foe.Type;
import zdream.pmfield.data.foe.VStat;
import zdream.pmfield.stage.buff.foe.ChargingMoveExecutingBuff;
import zdream.pmfield.stage.buff.foe.MoveExecutingBuff;

/**
 * @author Zdream
 * @date 2023-11-29
 * @since 0.0.1
 */
public class StageMove {
	public transient final FoeActor foe;
	public final MoveTemplate move;

	public StageMove(FoeActor foe, MoveTemplate move) {
		this.foe = Objects.requireNonNull(foe);
		this.move = Objects.requireNonNull(move);

		if (move.actionPoint == MoveTemplate.COST_ALL) {
			costAllActionPoint = true;
		} else {
			actionPoint = move.actionPoint;
		}
		if (move.movePoint == MoveTemplate.COST_ALL) {
			costAllMovePoint = true;
		} else {
			movePoint = move.movePoint;
		}

		this.types = move.types;
		this.attackerUsing = move.attackerUsing;
		this.defenseUsing = move.defenseUsing;
		this.target = move.target;
		if (move.additions != null) {
			this.additions = Arrays.copyOf(move.additions, move.additions.length);
		}

		this.vStats = move.vStats;
		for (MoveCheckPair pair : move.pairs) {
			if (pair.level == MoveCheckPair.LEVEL_MORE) {
				morePair = pair;
				continue;
			}
			pairs.put(pair.level, pair);
		}
		if (morePair == null) {
			morePair = new MoveCheckPair();
			morePair.level = MoveCheckPair.LEVEL_MORE;
		}
		this.checkRateFixed = move.checkRateFixed;

		parent = null;
		title = null;
		subMoves = handleSubMoves();
	}

	/* ***********
	 * Sub Moves *
	 *********** */
	public final StageMove[] subMoves;
	/**
	 * sub move only
	 */
	public final String title;
	public final StageMove parent;

	private StageMove[] handleSubMoves() {
		if (move.subs == null) {
			return null;
		}

		if (move.hasTags(MoveAdditionalEffects.T_CHARGING)) {
			this.executingBuffFactory = () -> new ChargingMoveExecutingBuff(this);
		} else {
			throw new IllegalStateException("不清楚这个 move:" + move + " 为什么要有 sub moves");
		}

		return Arrays.stream(move.subs)
				.map(sub -> new StageMove(this, sub))
				.toArray(StageMove[]::new);
	}

	private StageMove(StageMove parent, MoveTemplate.SubMove sub) {
		this.parent = Objects.requireNonNull(parent);
		this.foe = parent.foe;
		this.move = parent.move;
		this.subMoves = null;
		this.title = Objects.requireNonNull(sub.title);

		// subMove cost is 0
		this.types = parent.types;
		this.attackerUsing = parent.attackerUsing;
		this.defenseUsing = parent.defenseUsing;
		this.target = parent.target;
		this.additions = sub.additions;

		this.vStats = sub.vStats;
		for (MoveCheckPair pair : sub.pairs) {
			if (pair.level == MoveCheckPair.LEVEL_MORE) {
				morePair = pair;
				continue;
			}
			pairs.put(pair.level, pair);
		}
		if (morePair == null) {
			morePair = new MoveCheckPair();
			morePair.level = MoveCheckPair.LEVEL_MORE;
		}
		this.checkRateFixed = sub.checkRateFixed;
	}

	public StageMove findSubMove(String title) {
		if (subMoves == null) {
			return null;
		}

		return Arrays.stream(subMoves)
				.filter(sub -> sub.title.equals(title))
				.findAny()
				.orElse(null);
	}

	/* **************
	 * Action Point *
	 ************** */

	public byte actionPoint, movePoint;
	public boolean costAllActionPoint, costAllMovePoint;

	/* **********
	 *   Type   *
	 ********** */

	public Type.TypeWithWeight[] types;
	public MoveType attackerUsing, defenseUsing;
	public MoveTarget target;
	public String[] additions;

	public boolean hasTag(String tag) {
		return move.hasTags(tag) || (parent != null && parent.hasTag(tag));
	}

	public Type mainType() {
		for (Type.TypeWithWeight type : types) {
			if (type.weight >= Type.TypeWithWeight.WEIGHT_HIGH) {
				return type.type;
			}
		}
		throw new IllegalStateException("Fail to get main type of move: " + this);
	}

	public boolean hasType(Type type) {
		for (Type.TypeWithWeight moveType : types) {
			if (type == moveType.type) {
				return true;
			}
		}
		return false;
	}

	public float weightOfType(Type type) {
		for (Type.TypeWithWeight moveType : types) {
			if (type == moveType.type) {
				return moveType.weight / (float) Type.TypeWithWeight.WEIGHT_FULL;
			}
		}
		return 0;
	}

	/* **********
	 *  VStat   *
	 ********** */

	public VStat[] vStats;
	public Map<Byte, MoveCheckPair> pairs = new HashMap<>();
	public MoveCheckPair morePair;
	public int checkRateFixed;

	public MoveCheckPair findPair(byte level) {
		return pairs.get(level);
	}

	public MoveCheckPair findFullLevelPair() {
		return findPair((byte) vStats.length);
	}

	/* **********
	 *   Buff   *
	 ********** */
	public Supplier<MoveExecutingBuff> executingBuffFactory = () -> new MoveExecutingBuff(this);

	public MoveExecutingBuff createExecutingBuff() {
		return executingBuffFactory.get();
	}

	/* ***********
	 *  Others   *
	 *********** */

	@Override
	public String toString() {
		return move.toString();
	}
}
